package com.weixing.mall.core.converter;

import com.fasterxml.classmate.ResolvedType;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.weixing.mall.base.enums.IBaseEnum;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.Collections;
import springfox.documentation.schema.Enums;
import springfox.documentation.schema.Example;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.EnumTypeDeterminer;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger.readers.parameter.Examples;
import springfox.documentation.swagger.schema.ApiModelProperties;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class MyApiParamParameterBuilder implements ParameterBuilderPlugin {
    private final DescriptionResolver descriptions;
    private final EnumTypeDeterminer enumTypeDeterminer;

    private String desp;

    @Autowired
    public MyApiParamParameterBuilder(DescriptionResolver descriptions, EnumTypeDeterminer enumTypeDeterminer) {
        this.descriptions = descriptions;
        this.enumTypeDeterminer = enumTypeDeterminer;
    }

    @Override
    public void apply(ParameterContext context) {
        Optional<ApiParam> apiParam = context.resolvedMethodParameter().findAnnotation(ApiParam.class);
        AllowableValues allowableValues = this.allowableValues(context.alternateFor(context.resolvedMethodParameter().getParameterType()), (String) apiParam.transform(this.toAllowableValue()).or(""));
        context.parameterBuilder()
                .allowableValues(allowableValues);
        ApiParam annotation = null;
        if (apiParam.isPresent()) {
            annotation = (ApiParam) apiParam.get();
            context.parameterBuilder()
                    .name(Strings.emptyToNull(annotation.name()))
                    .description(Strings.emptyToNull(this.descriptions.resolve(annotation.value())))
                    .description(Strings.emptyToNull(this.descriptions.resolve(annotation.value())) + " " + this.desp)
                    .parameterAccess(Strings.emptyToNull(annotation.access()))
                    .defaultValue(Strings.emptyToNull(annotation.defaultValue()))
                    .allowMultiple(annotation.allowMultiple())
                    .allowEmptyValue(annotation.allowEmptyValue())
                    .required(annotation.required())
                    .scalarExample(new Example(annotation.example()))
                    .complexExamples(Examples.examples(annotation.examples()))
                    .hidden(annotation.hidden())
                    .collectionFormat(annotation.collectionFormat())
                    .order(-2147482648);
        }
    }

    private Function<ApiParam, String> toAllowableValue() {
        return new Function<ApiParam, String>() {
            @Override
            public String apply(ApiParam input) {
                return input.allowableValues();
            }
        };
    }

    private AllowableValues allowableValues(ResolvedType parameterType, String allowableValueString) {
        AllowableValues allowableValues = null;
        if (!Strings.isNullOrEmpty(allowableValueString)) {
            allowableValues = ApiModelProperties.allowableValueFromString(allowableValueString);
        } else {
            if (enumTypeDeterminer.isEnum(parameterType.getErasedType())) {
                allowableValues = allowableValues(parameterType.getErasedType());
            }

            if (Collections.isContainerType(parameterType)) {
                allowableValues = Enums.allowableValues(Collections.collectionElementType(parameterType).getErasedType());
            }
        }

        return allowableValues;
    }


    private AllowableValues allowableValues(Class<?> type) {
        if (type.isEnum()) {
            List<String> enumValues = getEnumValues(type);
            return new AllowableListValues(enumValues, "LIST");
        } else {
            return null;
        }
    }

    private List<String> getEnumValues(Class<?> subject) {
        IBaseEnum[] values = (IBaseEnum[]) subject.getEnumConstants();
        final List<String> displayValues = Lists.newArrayList();
        StringBuilder sb = new StringBuilder();
        for (IBaseEnum baseEnum : values) {
            Serializable value = baseEnum.getValue();
            String desp = baseEnum.getDesp();
            displayValues.add(String.valueOf(value));
            sb.append(value).append(":").append(desp);
        }

        this.desp = sb.toString();
        return displayValues;

    }


    @Override
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }
}